import { computed } from 'vue';
import { useStore, mapState, mapGetters } from 'vuex';
// mapActions, mapMutations 组件中可以直接使用原生写法，不需要封装

/**
 * 重新封装 mapState，使用方法与返回结果跟官方原生 mapState 一致，适用全局和 modules 局部
 * 推荐使用数组形式获取，写法更简洁
 *
 * 用法举例：
 *
 * 获取全局的 state 对象里的数据：
 * const { msg } = mapStateReset({msg: 'msg'});
 * const { msg } = mapStateReset(['msg']);
 *
 * 获取 modules 模块里的 hobby.js 的 state 数据
 * const { hobby, plan } = mapStateReset('hobby', {hobby: 'hobby', plan: 'plan'});
 * const { hobby, plan } = mapStateReset('hobby', ['hobby', 'plan']);
 */
const mapStateReset = (...args) => {
  const $store = useStore();
  const methodName = 'mapStateReset';
  const target = 'state';
  let modulesName = null;
  let params = null;
  const storeState = {};

  // 检测参数在 state 中是否存在
  const paramsCheckFn = (paramsValusArgs, $storeState) => {
    paramsValusArgs.forEach((item) => {
      if (!Object.keys($storeState).includes(item)) {
        throw new Error(`${methodName}方法里的参数"${item}" Vuex ${target} 中不存在`);
      }
    });
  };

  // 调用 mapState 的参数为对象
  const useObjectTypeFn = (params, $storeState) => {
    if (Object.prototype.toString.call(params) === '[object Object]') {
      const paramsValusArgs = Object.values(params);
      paramsCheckFn(paramsValusArgs, $storeState);
    }
  };

  // 调用 mapState 的参数为数组
  const useArrayTypeFn = (params, $storeState) => {
    if (Array.isArray(params)) {
      if (!params.length) throw new Error('${methodName}方法的数组参数长度不能为0');
      paramsCheckFn(params, $storeState);
    }
  };

  // computed 优化，给 mapState 返回的每个函数的 this 绑定 {$store} 对象
  const improveComputedFn = (storeStateFnsObj) => {
    Object.keys(storeStateFnsObj).forEach((key) => {
      const fn = storeStateFnsObj[key].bind({ $store });
      storeState[key] = computed(fn);
    });
  };

  if (args && args.length === 1) {
    // 用户调用全局 Vuex
    [params] = args;
    useObjectTypeFn(params, $store.state);
    useArrayTypeFn(params, $store.state);
    improveComputedFn(mapState(params));
  } else if (args && args.length === 2) {
    // 用户调用局部模块化 Vuex
    [modulesName, params] = args;
    useObjectTypeFn(params, $store.state[modulesName]);
    useArrayTypeFn(params, $store.state[modulesName]);
    improveComputedFn(mapState(modulesName, params));
  }
  return storeState;
};

/**
 * 重新封装 mapGetters，使用方法与返回结果跟官方原生 mapGetters 一致，适用全局和 modules 局部
 * 推荐使用数组形式获取，写法更简洁
 *
 * 用法举例：
 *
 * 获取全局的 state 对象里的数据：
 * const { newMsg } = mapGettersReset({newMsg: 'newMsg'});
 * const { newMsg } = mapGettersReset(['newMsg']);
 *
 * 获取 modules 模块里的 hobby.js 的 state 数据
 * const { totalHobby } = mapGettersReset('hobby', {totalHobby: 'totalHobby'});
 * const { totalHobby } = mapGettersReset({'hobby', ['totalHobby']);
 */
const mapGettersReset = (...args) => {
  const $store = useStore();
  const methodName = 'mapGettersReset';
  const target = 'getters';
  let modulesName = null;
  let params = null;
  const storeGetters = {};

  // 检测参数在 getters 中是否存在
  const paramsCheckFn = (paramsValusArgs, isModulesName) => {
    // 检测参数在 getters 中是否存在
    paramsValusArgs.forEach((item) => {
      if (isModulesName) {
        // eslint-disable-next-line no-param-reassign
        item = `${modulesName}/${item}`;
      }
      if (!Object.keys($store.getters).includes(item)) {
        throw new Error(`${methodName}方法里的参数"${item}" Vuex ${target} 中不存在`);
      }
    });
  };

  // 调用 mapGetters 的参数为对象
  const useObjectTypeFn = (params, modulesName) => {
    if (Object.prototype.toString.call(params) === '[object Object]') {
      const paramsValusArgs = Object.values(params);
      paramsCheckFn(paramsValusArgs, modulesName);
    }
  };

  // 调用 mapGetters 的参数为数组
  const useArrayTypeFn = (params, modulesName) => {
    if (Array.isArray(params)) {
      if (!params.length) throw new Error('${methodName}方法的数组参数长度不能为0');
      paramsCheckFn(params, modulesName);
    }
  };

  // computed 优化，给 mapGetters 返回的每个函数的 this 绑定 {$store} 对象
  const improveComputedFn = (storeGettersFnsObj) => {
    Object.keys(storeGettersFnsObj).forEach((key) => {
      const fn = storeGettersFnsObj[key].bind({ $store });
      storeGetters[key] = computed(fn);
    });
  };

  if (args && args.length === 1) {
    // 用户调用全局 Vuex
    [params] = args;
    useObjectTypeFn(params);
    useArrayTypeFn(params);
    improveComputedFn(mapGetters(params));
  } else if (args && args.length === 2) {
    // 用户调用局部模块化 Vuex
    [modulesName, params] = args;
    useObjectTypeFn(params, modulesName);
    useArrayTypeFn(params, modulesName);
    improveComputedFn(mapGetters(modulesName, params));
  }
  return storeGetters;
};

export { mapStateReset, mapGettersReset };
